package titt.service.proxy.server;

import titt.TITTValues;
import titt.head.HTTPHead;
import titt.head.HeadFactory;
import titt.head.request.MethodHead;
import titt.head.request.Request;
import titt.head.response.ResponseHead;
import titt.io.IOFactory;
import titt.io.encoding.EncodingFacade;
import titt.io.socket.TITTSocket;
import titt.io.writer.ResponseWriter;
import titt.service.ServicePackage;
import titt.service.TITTInternal;
import titt.service.proxy.TITTInvoker;
import titt.service.threading.MultipleTITTThreader;
import titt.type.Type;
import titt.type.TypedObject;
import titt.type.Types;
import titt.util.ArrayUtil;

public class ServerProxy {

	private final String ID = "titt";
	private final String[] ID_TOKENS = {ID};
	
	private final EncodingFacade ef = IOFactory.createEncodingFacade();
	private final TITTInvoker inv = new TITTInvoker(ID);
	private final TITTInternal internal = new TITTInternal();
	
	private static final ServerProxy sp = new ServerProxy();
	
	private ServerProxy() {}
	
	public static ServerProxy get() {
		return sp;
	}

	public String register(Object o, String... tokens) throws Exception {
		TITTInvoker inv = new TITTInvoker(ArrayUtil.combine(ID_TOKENS, tokens));	
		return inv.call("register", new TypedObject[]{internal.call(o, TITTInternal.INTERFACE)}).getObject();
	}
	
	public String registerWithLink(Object o, String link, String... tokens) throws Exception {
		TITTInvoker inv = new TITTInvoker(ArrayUtil.combine(ID_TOKENS, tokens));	
		return inv.call("register", new TypedObject[]{internal.call(o, TITTInternal.INTERFACE), new TypedObject(Types.STRING, link)}).getObject();
	}
	
	
	public TITTSocket sync(String packageID) throws Exception {
		TITTSocket socket = IOFactory.createTITTSocket();

		try  {
			socket.getHeadWriter().writeHead(createTITTCallHead(TITTValues.POST, "sync", packageID));
			return socket;
		}
		catch(Exception e) {
			close(socket);
			throw e;
		}	
	}

	public ServicePackage withdraw(String serviceID) throws Exception {
		TITTSocket socket = IOFactory.createTITTSocket();

		try {
			socket.getHeadWriter().writeHead(createTITTCallHead(TITTValues.GET, "withdraw", serviceID));
			ResponseHead h = new ResponseHead(socket.getHeadReader().readHead());

			if(h.getStatusCode() != 200) return null;

			ServicePackage pack = new ServicePackage(h.getHeader("E-Tag").getValue(), socket.getHeadReader().readHead(), socket);

			return pack;
		}
		catch(Exception e) {
			close(socket);
			throw e;
		}
	}
	
	public void unregister(String serviceID, boolean lazy) throws Exception {
		TITTSocket socket = IOFactory.createTITTSocket();

		try {
			socket.getHeadWriter().writeHead(createTITTCallHead(TITTValues.GET, "unregister", serviceID, lazy));
			socket.getHeadReader().readHead();
		}
		catch(Exception e) {
			close(socket);
			throw e;
		}

		close(socket);
	}
	
	public void detectErrors(final String serviceID) {
		MultipleTITTThreader.get().execute(new Runnable() {
			public void run() {				
				TITTSocket ts = null;
				try {
					
					ts = IOFactory.createTITTSocket();
					ts.getHeadWriter().writeHead(createTITTCallHead(TITTValues.GET, "error", serviceID));
					ts.getHeadReader().readHead();
					
				}
				catch(Exception e) {
					
				}
				close(ts);
			}
		});
	}

	public void sync(int status, TypedObject ret, String packageID) {
		TITTSocket syncSocket = null;
		
		try {
			syncSocket = sync(packageID);

			if(ret != null) {
				ResponseWriter rw = new ResponseWriter(syncSocket, ef, new ResponseHead(status, HeadFactory.EMPTY));	
				rw.writeValues(ret);
			}
			else
				syncSocket.getHeadWriter().writeHead(new ResponseHead(status, HeadFactory.EMPTY));
		}
		catch(Exception e) {}
		
		close(syncSocket);
	}
	
	public void sync(int status, String packageID) {
		sync(status, null, packageID);		
	}
	
	public String[] listServices() throws Exception {
		return inv.call("list").getObject();
	}
	
	public String gensym() throws Exception {
		return inv.call("gensym").getObject();
	}
	
	private HTTPHead createTITTCallHead(int method, String methodName, Object... args) {
		TypedObject[] tos = new TypedObject[args.length];

		for(int i = 0; i < args.length; i++)
			tos[i] = new TypedObject(new Type(args[i]), args[i]);

		return new MethodHead(methodName, tos, new TypedObject[0], new Request(method, "/"+ID+"/", HeadFactory.EMPTY));
	}
	
	private void close(TITTSocket socket) {
		try {
			if(socket != null && !socket.isClosed())
				socket.close();
		}
		catch(Exception e) {}
	}

}
